from fastapi import FastAPI
from starlette.responses import JSONResponse
from SqlalchemyConfig import SessionLocal
from models.Venue import Venue
from models.Security import Security
from models.Unit import Unit
from models.Resources import Resources
from vo.CoordinateRequest import CoordinateRequest
from vo.CoordinateRequest2 import CoordinateRequest2
import uvicorn
import requests
import logging
import configparser
import os
import threading
import json

app = FastAPI()
logger = logging.getLogger(__name__)

# 数据库依赖（用于获取 session）
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

from sqlalchemy.orm import Session
from sqlalchemy import select

def get_security_with_unit_coords(db: Session):
    stmt = (
        select(
            Security.id,
            Security.title,
            Security.tel,
            Security.status,
            Unit.zuobiao_lng,
            Unit.zuobiao_lat,
            Unit.title
        )
        .join(Unit, Security.cid == Unit.id)
    )
    return db.execute(stmt).all()


def read_ai_url(config_file='config.ini'):
    """从配置文件中读取基础 URL 和 API 路径"""
    config = configparser.ConfigParser()
    if not os.path.exists(config_file):
        logger.error(f"❌ 配置文件 {config_file} 不存在")
        return None

    try:
        config.read(config_file, encoding='utf-8')

        ai_url = config.get('BaseApi', 'ai_url', fallback=None)

        if not ai_url:
            logger.error("❌ 配置文件中未找到 base_url")
            return None

        return ai_url

    except Exception as e:
        logger.error(f"❌ 读取配置失败：{str(e)}", exc_info=True)
        return None


def get_ai_response(full_input):
    try:
        ai_url = read_ai_url()
        headers = {"Content-Type": "application/json"}

        # Step 1: 创建 chat 上下文
        create_response = requests.post(
            ai_url + "/chat/create_chat",
            json={
                "model": "qwen3",
                "parameters": "4b",
                "title": "资源调度和避难方案"
            },
            headers=headers
        )
        create_response.raise_for_status()
        context_id = create_response.json()["message"]["context_id"]

        # Step 2: 发送用户输入进行聊天
        chat_response = requests.post(
            ai_url + "/chat/chat",
            json={
                "model": "qwen3",
                "parameters": "4b",
                "supplierName": "ollama",
                "context_id": context_id,
                "search": "",
                "rag_list": "[]",
                "temp_chat": "true",
                "mcp_servers": [],
                "user_content": full_input,
                "images": "",
                "doc_files": ""
            },
            headers=headers
        )
        chat_response.raise_for_status()

        # Step 3: 获取最后一次聊天记录
        history_response = requests.post(
            ai_url + "/chat/get_last_chat_history",
            json={"context_id": context_id},
            headers=headers
        )
        history_response.raise_for_status()
        content = history_response.json()["message"]["content"]

        # Step 4: 异步执行清理操作（不阻塞主流程）
        threading.Thread(
            target=cleanup_context,
            args=(ai_url, context_id),
            daemon=True  # 主线程结束时该线程自动结束
        ).start()

        # 立即返回结果
        return {"code": 200, "content": content}

    except requests.exceptions.RequestException as e:
        return {"code": 400, "error": f"网络请求失败: {str(e)}"}
    except KeyError as e:
        return {"code": 400, "error": f"响应缺少必要字段: {str(e)}"}
    except Exception as e:
        return {"code": 400, "error": f"未知错误: {str(e)}"}


def cleanup_context(ai_url, context_id):
    """清理上下文的函数，供异步线程调用"""
    try:
        headers = {"Content-Type": "application/json"}
        remove_response = requests.post(
            ai_url + "/chat/remove_chat",
            json={"context_id": context_id},
            headers=headers
        )
        remove_response.raise_for_status()
    except Exception as e:
        # 清理失败可以记录日志或忽略
        print(f"清理上下文失败：{str(e)}")

def format_venue_for_nlp(venues):
    prompt_lines = ["以下是避难场所信息："]

    for idx, v in enumerate(venues, 1):
        # 转换 status 数字到中文
        status_text = "可用" if v.status == 1 else "不可用"

        line = (
            f"{idx}. 名称：{v.title or '未知名称'}，"
            f"经度：{float(v.zuobiao_lng) if v.zuobiao_lng else '未知'}，"
            f"纬度：{float(v.zuobiao_lat) if v.zuobiao_lat else '未知'}，"
            f"容量：{v.capacity or 0}人，"
            f"状态：{status_text}"
        )
        prompt_lines.append(line)

    return "\n".join(prompt_lines)


def format_resources_for_nlp(resources):
    prompt_lines = ["以下是物资点信息："]

    for idx, r in enumerate(resources, 1):
        status_text = "可用" if r.status == 1 else "不可用"

        line = (
            f"{idx}. 名称：{r.title or '未知名称'}，"
            f"经度：{float(r.zuobiao_lng) if r.zuobiao_lng else '未知'}，"
            f"纬度：{float(r.zuobiao_lat) if r.zuobiao_lat else '未知'}，"
            f"状态：{status_text}"
        )
        prompt_lines.append(line)

    return "\n".join(prompt_lines)


def format_security_for_nlp(resources):
    prompt_lines = ["以下是应急单位与救援队伍负责人信息："]

    for idx, r in enumerate(resources, 1):
        sec_title = r[1] or "未知名称"
        sec_tel = r[2] or "无联系方式"
        sec_status = r[3]
        unit_lng = r[4]
        unit_lat = r[5]
        unit_title = r[6]

        status_text = "可用" if sec_status == 1 else "不可用"

        line = (
            f"{idx}. 负责人名称：{sec_title}，"
            f"经度：{float(unit_lng):.6f}，"
            f"纬度：{float(unit_lat):.6f}，"
            f"电话：{sec_tel}，"
            f"所属单位：{unit_title}，"
            f"状态：{status_text}"
        )

        prompt_lines.append(line)

    return "\n".join(prompt_lines)


@app.post("/generate")
async def generate_text(request: CoordinateRequest):
    db = next(get_db())
    venues = db.query(Venue).all()
    resources = db.query(Resources).all()
    # 把数据库数据转成 NLP 可理解的文本
    venues_prompt = format_venue_for_nlp(venues)
    resources_prompt = format_resources_for_nlp(resources)
    security = get_security_with_unit_coords(db)
    security_prompt = format_security_for_nlp(security)
    # 构建完整输入文本
    full_input = (
        "你是灾害应急处置系统助手，请根据以下避难场所和物资点信息回答问题：\n"
        f"{venues_prompt}\n"
        f"{resources_prompt}\n"
        f"{security_prompt}\n"
        f"我所在的坐标是：经度{request.zuobiao_lng}，纬度{request.zuobiao_lat}\n"
        f"你帮我规划该坐标最优的资源调度和避难方案即可，无需重复列出我提供的数据，也不要问我是否有其他需求。"
    )
    content = get_ai_response(full_input)
    if content is not None and content["code"] == 200:
        return JSONResponse({"msg": "方案生成成功",
                             "content": content["content"]}, status_code=content["code"])
    else:
        return JSONResponse({"msg": content["error"]}, status_code=content["code"])

@app.post("/generate2")
async def generate2_text(request: CoordinateRequest2):
    full_input = (
        "你是灾害预警专家，你要根据我的提问，尽可能细致的，准确的，以json列表的格式输出相关回答（一行字符串即可），因为我需要用代码二次处理你的回答。" +
        "你的回答模板是每个步骤的内容（字段是text）+ 生成图片所需的提示词，图片风格为卡通（字段是prompt），问题如下：" + request.text
    )
    content = get_ai_response(full_input)
    json_str = content["content"]
    json_str_clean = json_str.strip()
    try:
        steps = json.loads(json_str_clean)
    except json.JSONDecodeError as e:
        print("JSON 解析失败:", e)
        steps = []
    if content is not None and content["code"] == 200:
        return JSONResponse({"msg": "图文生成成功",
                             "content": steps}, status_code=content["code"])
    else:
        return JSONResponse({"msg": content["error"]}, status_code=content["code"])

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)